﻿' CRMHelper
'
' Various helper functions for CRM Queue Manager

Imports QueueManager.Helper.CRM
Imports QueueManager.Helper.CRMMeta
Imports System.Configuration
Imports System.Net
Imports System.Net.Mail
Imports System.Text.RegularExpressions

Public Structure NameStruct
    Public FirstName As String
    Public LastName As String
End Structure

Public Enum PartType
    Fullname
    FirstName
    LastName
    CompanyName
    Url
End Enum

Public Class CRMHelper

    Private _objCrm As CrmService
    Private _objOrg As organization
    Private _intDescLength As Integer
    Private _objMeta As New Collection

    ''' <summary>
    ''' Returns an initialized handle to the CrmService
    ''' </summary>
    Public Function GetCrmService() As CrmService

        Dim objToken As CRM.CrmAuthenticationToken
        Dim objDisco As CRMDisco.CrmDiscoveryService
        Dim objPolicyReq As CRMDisco.RetrievePolicyRequest
        Dim objPolicyRes As CRMDisco.RetrievePolicyResponse
        Dim objLogon As Passport.LogonManager
        Dim strTicket As String
        Dim objTicketReq As CRMDisco.RetrieveCrmTicketRequest
        Dim objTicketRes As CRMDisco.RetrieveCrmTicketResponse

        If _objCrm IsNot Nothing Then
            Return _objCrm
        End If

        'Should we connect to CRM Online or the on-premise edition? This is determined by
        'checking the url. Not a perfect solution but fairly backward compatible.
        If ConfigurationManager.AppSettings("crmserviceurl").ToLower.Contains("crmdiscoveryservice.asmx") Then
            'Access the discovery service to get the ticket
            objDisco = New CRMDisco.CrmDiscoveryService
            objDisco.Url = ConfigurationManager.AppSettings("crmserviceurl")
            objPolicyReq = New CRMDisco.RetrievePolicyRequest
            objPolicyRes = CType(objDisco.Execute(objPolicyReq), CRMDisco.RetrievePolicyResponse)

            'Logon to passport
            objLogon = New Passport.LogonManager

            strTicket = objLogon.Logon(ConfigurationManager.AppSettings("username"), _
                                       ConfigurationManager.AppSettings("password"), _
                                       "crm.dynamics.com", _
                                       objPolicyRes.Policy, _
                                       "Production")

            objLogon.Dispose()

            'Get a ticket
            objTicketReq = New CRMDisco.RetrieveCrmTicketRequest

            objTicketReq.OrganizationName = ConfigurationManager.AppSettings("organization")
            objTicketReq.PassportTicket = strTicket

            objTicketRes = CType(objDisco.Execute(objTicketReq), CRMDisco.RetrieveCrmTicketResponse)

            objToken = New CRM.CrmAuthenticationToken With {.AuthenticationType = 1, _
                                                            .OrganizationName = ConfigurationManager.AppSettings("organization"), _
                                                            .CrmTicket = objTicketRes.CrmTicket}
            _objCrm = New CrmService
            _objCrm.Url = objTicketRes.OrganizationDetail.CrmServiceUrl
            _objCrm.CrmAuthenticationTokenValue = objToken

        Else

            objToken = New CRM.CrmAuthenticationToken With {.AuthenticationType = 0, .OrganizationName = ConfigurationManager.AppSettings("organization")}
            _objCrm = New CrmService

            _objCrm.PreAuthenticate = True
            _objCrm.Credentials = New NetworkCredential(ConfigurationManager.AppSettings("username"), ConfigurationManager.AppSettings("password"), ConfigurationManager.AppSettings("domain"))
            _objCrm.Url = ConfigurationManager.AppSettings("crmserviceurl")
            _objCrm.CrmAuthenticationTokenValue = objToken
        End If

        Return _objCrm

    End Function

    ''' <summary>
    ''' Get Organizational info
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetOrgInfo() As organization

        Dim objWhoAmIReq As WhoAmIRequest
        Dim objWhoAmIRes As WhoAmIResponse

        If _objOrg IsNot Nothing Then
            Return _objOrg
        End If

        objWhoAmIReq = New WhoAmIRequest
        objWhoAmIRes = CType(GetCrmService.Execute(objWhoAmIReq), WhoAmIResponse)

        _objOrg = CType(GetSingleData(EntityName.organization.ToString, objWhoAmIRes.OrganizationId, Nothing), organization)

        Return _objOrg

    End Function

    Public Function GetMetadata() As MetadataService

        Dim objMetadata As MetadataService

        objMetadata = New MetadataService
        objMetadata.CrmAuthenticationTokenValue = New CRMMeta.CrmAuthenticationToken With {.AuthenticationType = 0, .OrganizationName = ConfigurationManager.AppSettings("organization")}
        objMetadata.PreAuthenticate = True
        objMetadata.Credentials = New NetworkCredential(ConfigurationManager.AppSettings("username"), ConfigurationManager.AppSettings("password"), ConfigurationManager.AppSettings("domain"))
        objMetadata.Url = ConfigurationManager.AppSettings("crmserviceurl").ToLower.Replace("crmservice.asmx", "metadataservice.asmx")

        Return objMetadata

    End Function

    ''' <summary>
    ''' Returns the metadata for an attribute
    ''' </summary>
    ''' <param name="Name">The name of the property</param>
    Public Function GetAttribute(ByVal TheEntity As String, ByVal Name As String) As AttributeMetadata

        Dim objRequest As RetrieveEntityRequest
        Dim objResponse As RetrieveEntityResponse

        If Not _objMeta.Contains(TheEntity) Then
            objRequest = New RetrieveEntityRequest
            objRequest.LogicalName = TheEntity
            objRequest.EntityItems = EntityItems.All
            objResponse = CType(GetMetadata.Execute(objRequest), RetrieveEntityResponse)

            _objMeta.Add(objResponse.EntityMetadata, TheEntity)
        End If

        For Each objAtt In CType(_objMeta(TheEntity), EntityMetadata).Attributes
            If objAtt.LogicalName.ToLower = Name.ToLower Then
                Return objAtt
            End If
        Next

        Return Nothing

    End Function

    ''' <summary>
    ''' Resolve a parent account based on the e-mail address
    ''' </summary>
    ''' <param name="Contact">The contact</param>
    ''' <param name="Address">The e-mail address</param>
    ''' <param name="ResolveMailFrom">The from address when sending resolve e-mail.</param>
    ''' <param name="ResolveMailTo">The to address to send e-mail to.</param>
    Public Function ResolveParentAccount(ByVal Contact As contact, _
                                         ByVal Address As String, _
                                         ByVal ResolveMailTo As String, _
                                         ByVal ResolveMailFrom As String) As contact

        Dim objContactQuery As New QueryExpressionHelper(EntityName.contact.ToString)
        Dim strDomain As String
        Dim objEntities As BusinessEntityCollection
        Dim objCustomer As Customer

        'Get the domain part 
        If Address.Split("@"c).Length = 2 Then
            strDomain = "%@" & Address.Split("@"c)(1)
        Else
            Return Contact
        End If

        'Retrieve parent customer id
        objContactQuery.Columns.AddColumns("parentcustomerid")

        'Only sample 10 first contacts
        objContactQuery.PageInfo = New PagingInfo
        objContactQuery.PageInfo.Count = 10
        objContactQuery.PageInfo.PageNumber = 1

        'Attempt to lookup contact using email1
        objContactQuery.Criteria.FilterOperator = LogicalOperator.And
        objContactQuery.Criteria.Conditions.AddCondition("emailaddress1", ConditionOperator.Like, strDomain)

        'Execute query
        Try
            objEntities = GetCrmService.RetrieveMultiple(objContactQuery.Query)
        Catch ex As Exception
            LogHelper.Log("Error retrieving contacts when resolving parent account.", ex, LogHelper.LogType.Exception)
            Return Contact
        End Try

        'Loop through and figure out the parent account for contacts with same e-mail domain.
        'Only do this if all contacts return same contact.
        For Each objContact As contact In objEntities.BusinessEntities
            If objContact.parentcustomerid IsNot Nothing Then

                'Right, let's see if we have already found a customer
                If objCustomer Is Nothing Then
                    objCustomer = objContact.parentcustomerid
                Else
                    'If the current customer differs then send mail and return the contact unchanged
                    If objCustomer.Value <> objContact.parentcustomerid.Value Then

                        If Not String.IsNullOrEmpty(ResolveMailTo) Then
                            'Send an e-mail
                            Try
                                SendNetMail("CRM Queue Manager parent account matching (failed)", _
                                            String.Format("More than one account was found for contact '{0}'. Accounts found: {1}{2}{3}{4}", Contact.emailaddress1, vbCrLf & vbCrLf, objCustomer.name, vbCrLf, objContact.parentcustomerid.name), _
                                            ResolveMailTo, _
                                            "", _
                                            ResolveMailFrom, _
                                            False)
                            Catch ex As Exception
                                'Catch and move on
                                LogHelper.Log("Error sending resolve mail (1).", ex, LogHelper.LogType.Exception)
                            End Try

                            'Return the contact unchanged
                            Return Contact
                        End If
                    End If
                End If

            End If
        Next

        'Let's see if we have found a customer and update the parentcustomerid if we have.
        If objCustomer IsNot Nothing Then
            Contact.parentcustomerid = New Customer
            Contact.parentcustomerid.type = objCustomer.type
            Contact.parentcustomerid.Value = objCustomer.Value

            If Not String.IsNullOrEmpty(ResolveMailTo) Then
                'Send an e-mail
                Try
                    SendNetMail("CRM Queue Manager parent account matching (success)", _
                                String.Format("Automatically matched parent account for contact '{0} {1} ({2})'. Accounts is set to '{3}'.", Contact.firstname, Contact.lastname, Contact.emailaddress1, objCustomer.name), _
                                ResolveMailTo, _
                                "", _
                                ResolveMailFrom, _
                                False)
                Catch ex As Exception
                    'Catch and move on
                    LogHelper.Log("Error sending resolve mail (2).", ex, LogHelper.LogType.Exception)
                End Try
            End If
        End If

        Return Contact

    End Function

    ''' <summary>
    ''' Return a resolved part of an e-mail address.
    ''' </summary>
    ''' <param name="Address">E-Mail to resolve</param>
    ''' <param name="TypeToReturn">The part to resolve</param>
    Public Function GetPartFromEMail(ByVal Address As String, ByVal TypeToReturn As PartType) As String

        Dim strString As String = Address.Trim
        Dim strReturn As String
        Dim strTemp1 As String
        Dim strTemp2 As String

        If strString.IndexOf("@") < 0 Then
            Return ""
        End If

        strTemp1 = strString.Substring(0, strString.IndexOf("@"))
        strTemp2 = strString.Replace(strTemp1 & "@", "")

        Select Case TypeToReturn
            'Return fullname
            Case PartType.Fullname

                If strTemp1.IndexOf(".") > 0 Then
                    'Firstname
                    strTemp2 = StrConv(strTemp1.Substring(0, strTemp1.IndexOf(".")), VbStrConv.ProperCase)
                    'Lastname
                    strTemp1 = StrConv(strTemp1.Substring(strTemp1.IndexOf(".") + 1, strTemp1.Length - strTemp1.IndexOf(".") - 1), VbStrConv.ProperCase)

                    strReturn = strTemp2 & " " & strTemp1
                Else
                    strReturn = StrConv(strTemp1, VbStrConv.ProperCase)
                End If

                Return strReturn.Trim

            Case PartType.FirstName

                If strTemp1.IndexOf(".") > 0 Then
                    'Firstname
                    strReturn = StrConv(strTemp1.Substring(0, strTemp1.IndexOf(".")), VbStrConv.ProperCase)
                Else
                    strReturn = ""
                End If

                Return strReturn.Trim

            Case PartType.LastName

                If strTemp1.IndexOf(".") > 0 Then
                    'Lastname
                    strReturn = StrConv(strTemp1.Substring(strTemp1.IndexOf(".") + 1, strTemp1.Length - strTemp1.IndexOf(".") - 1), VbStrConv.ProperCase)
                Else
                    strReturn = StrConv(strTemp1, VbStrConv.ProperCase)
                End If

                Return strReturn.Trim

            Case PartType.CompanyName

                If strTemp2.IndexOf(".") > 0 Then
                    'Companyname
                    strReturn = StrConv(strTemp2.Substring(0, strTemp2.IndexOf(".")), VbStrConv.ProperCase)
                Else
                    strReturn = StrConv(strTemp2, VbStrConv.ProperCase)
                End If

                Return strReturn.Trim

            Case PartType.Url
                Return "http://www." & strTemp2

            Case Else
                Return ""

        End Select

    End Function

    ''' <summary>
    ''' Cleans up HTML and returns plain text
    ''' </summary>
    ''' <param name="Source">The HTML to clean</param>
    Public Function CleanHTML(ByVal Source As String) As String

        Dim strResult As String

        Try

            ' Replace line breaks with space
            ' because browsers inserts space
            strResult = Source.Replace(Chr(13), " ")

            ' Replace line breaks with space
            ' because browsers inserts space
            strResult = strResult.Replace(Chr(10), " ")

            ' Remove step-formatting
            strResult = strResult.Replace(Chr(9), String.Empty)

            ' Remove repeating spaces becuase browsers ignore them
            strResult = Regex.Replace(strResult, "( )+", " ")

            ' Remove the header (prepare first by clearing attributes)
            strResult = Regex.Replace(strResult, "<( )*head([^>])*>", "<head>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<( )*(/)( )*head( )*>)", "</head>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<head>).*(</head>)", String.Empty, RegexOptions.IgnoreCase)

            ' remove all scripts (prepare first by clearing attributes)
            strResult = Regex.Replace(strResult, "<( )*script([^>])*>", "<script>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<( )*(/)( )*script( )*>)", "</script>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<script>).*(</script>)", String.Empty, RegexOptions.IgnoreCase)

            ' remove all styles (prepare first by clearing attributes)
            strResult = Regex.Replace(strResult, "<( )*style([^>])*>", "<style>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<( )*(/)( )*style( )*>)", "</style>", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(<style>).*(</style>)", String.Empty, RegexOptions.IgnoreCase)

            ' insert tabs in spaces of <td> tags
            strResult = Regex.Replace(strResult, "<( )*td([^>])*>", Chr(9), RegexOptions.IgnoreCase)

            ' insert line breaks in places of <BR> and <LI> tags
            strResult = Regex.Replace(strResult, "<( )*br( )*>", Chr(13), RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "<( )*li( )*>", Chr(13), RegexOptions.IgnoreCase)

            ' insert line paragraphs (double line breaks) in place
            ' if <P>, <DIV> and <TR> tags
            strResult = Regex.Replace(strResult, "<( )*div([^>])*>", Chr(13) & Chr(13), RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "<( )*tr([^>])*>", Chr(13) & Chr(13), RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "<( )*p([^>])*>", Chr(13) & Chr(13), RegexOptions.IgnoreCase)

            ' Remove remaining tags like <a>, links, images,
            ' comments etc - anything thats enclosed inside < >
            strResult = Regex.Replace(strResult, "<[^>]*>", String.Empty, RegexOptions.IgnoreCase)

            ' replace special characters:
            strResult = Regex.Replace(strResult, " ", " ", RegexOptions.IgnoreCase)

            strResult = Regex.Replace(strResult, "&bull;", " * ", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&lsaquo;", "<", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&rsaquo;", ">", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&trade;", "(tm)", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&frasl;", "/", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&lt;", "<", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&gt;", ">", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&copy;", "(c)", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&reg;", "(r)", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "&(.{2,6});", String.Empty, RegexOptions.IgnoreCase)

            ' make line breaking consistent
            strResult = strResult.Replace(Chr(10), Chr(13))

            ' Remove extra line breaks and tabs:
            ' replace over 2 breaks with 2 and over 4 tabs with 4. 
            ' Prepare first to remove any whitespaces inbetween
            ' the escaped characters and remove redundant tabs inbetween linebreaks
            strResult = Regex.Replace(strResult, "(" & Chr(13) & ")( )+(" & Chr(13) & ")", "" & Chr(13) & "" & Chr(13) & "", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(" & Chr(9) & ")( )+(" & Chr(9) & ")", "" & Chr(9) & "" & Chr(9) & "", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(" & Chr(9) & ")( )+(" & Chr(13) & ")", "" & Chr(9) & "" & Chr(13) & "", RegexOptions.IgnoreCase)
            strResult = Regex.Replace(strResult, "(" & Chr(13) & ")( )+(" & Chr(9) & ")", "" & Chr(13) & "" & Chr(9) & "", RegexOptions.IgnoreCase)

            ' Remove redundant tabs
            strResult = Regex.Replace(strResult, "(" & Chr(13) & ")(" & Chr(9) & ")+(" & Chr(13) & ")", "" & Chr(13) & "" & Chr(13) & "", RegexOptions.IgnoreCase)

            ' Remove multible tabs followind a linebreak with just one tab
            strResult = Regex.Replace(strResult, "(" & Chr(13) & ")(" & Chr(9) & ")+", "" & Chr(13) & "" & Chr(9) & "", RegexOptions.IgnoreCase)

            strResult = strResult.Replace(Chr(13) & Chr(13), Chr(13))
            strResult = strResult.Replace(Chr(9) & Chr(9), Chr(9))
            strResult = strResult.Replace("  ", " ")

            Return strResult

        Catch ex As Exception
            LogHelper.Log("Error clearing HTML.", ex, LogHelper.LogType.Warning)
            Return Source
        End Try

    End Function


    ''' <summary>
    ''' Resolves names in an e-mail address.
    ''' </summary>
    ''' <param name="Name">The e-mail address to resolve names for.</param>
    Public Function ResolveNames(ByVal Name As String) As NameStruct

        Dim strName As String
        Dim objNewName As New NameStruct

        'Remove " and ' from e-mail
        Name = Name.Replace(Chr(34), "").Replace("'", "")

        If Name.IndexOf("<") > 0 Then
            strName = Name.Substring(0, Name.IndexOf("<") - 1).Trim
            If strName.Contains(" ") Then
                objNewName.FirstName = strName.Split(" "c)(0).Trim
                objNewName.LastName = strName.Split(" "c)(1).Trim
            Else
                objNewName.FirstName = strName
            End If
        End If

        If objNewName.FirstName = "" Then
            objNewName.FirstName = GetPartFromEMail(Name, PartType.FirstName)
            objNewName.LastName = GetPartFromEMail(Name, PartType.LastName)
        End If

        Return objNewName

    End Function

    ''' <summary>
    ''' Sends e-mail using .NET Framework Smtp classes
    ''' </summary>
    ''' <param name="Subject">The subject</param>
    ''' <param name="Body">The body</param>
    ''' <param name="SendTo">Recipients</param>
    ''' <param name="SendToBcc">Blind copies</param>
    ''' <param name="SendFrom">From address</param>
    ''' <param name="Html">As HTML?</param>
    Public Sub SendNetMail(ByVal Subject As String, ByVal Body As String, ByVal SendTo As String, ByVal SendToBcc As String, ByVal SendFrom As String, ByVal Html As Boolean)

        Dim objMailMsg As New MailMessage(SendFrom, SendTo)
        Dim objSmtp As New SmtpClient

        objMailMsg.Body = Body
        objMailMsg.Subject = Subject
        objMailMsg.IsBodyHtml = Html

        If Not String.IsNullOrEmpty(SendToBcc) Then
            objMailMsg.Bcc.Add(SendToBcc)
        End If

        'Workaround for Google
        If objSmtp.Host.Contains("gmail") Then
            objSmtp.EnableSsl = True
        End If

        objSmtp.Send(objMailMsg)

    End Sub

    ''' <summary>
    ''' Sends e-mail using CRM
    ''' </summary>
    ''' <param name="Subject">The subject</param>
    ''' <param name="Body">The body</param>
    ''' <param name="SendTo">The to entity</param>
    ''' <param name="SendFromID">The from entity ID</param>
    ''' <param name="FromType">The from type</param>
    ''' <param name="objCase">The case to relate to</param>
    ''' <remarks></remarks>
    Public Sub SendCrmMail(ByVal Subject As String, _
                           ByVal Body As String, _
                           ByVal SendTo As IncomingEntity, _
                           ByVal SendFromID As String, _
                           ByVal FromType As String, _
                           ByVal objCase As DynamicEntity)

        Dim objEmail As New email
        Dim objFrom As New activityparty
        Dim objTo As New activityparty
        Dim objEMailID As Guid
        Dim objTrackingRequest As New GetTrackingTokenEmailRequest
        Dim objTrackingResponse As New GetTrackingTokenEmailResponse
        Dim objSend As New SendEmailRequest

        objEmail.subject = Subject
        objEmail.description = Body

        objFrom.partyid = New Lookup
        objFrom.partyid.type = FromType
        objFrom.partyid.Value = New Guid(SendFromID)

        objTo.partyid = New Lookup
        objTo.partyid.type = SendTo.Type
        objTo.partyid.Value = SendTo.ID

        objEmail.from = New activityparty() {objFrom}
        objEmail.to = New activityparty() {objTo}

        'Update the e-mail and set "regarding" to the case
        If objCase IsNot Nothing Then
            objEmail.regardingobjectid = New Lookup
            objEmail.regardingobjectid.type = EntityName.incident.ToString
            objEmail.regardingobjectid.Value = New Guid(objCase.GetValue("incidentid"))
        End If

        objEMailID = GetCrmService.Create(objEmail)

        objTrackingRequest.Subject = Subject
        objTrackingResponse = CType(GetCrmService.Execute(objTrackingRequest), GetTrackingTokenEmailResponse)

        objSend.EmailId = objEMailID
        objSend.IssueSend = True
        objSend.TrackingToken = objTrackingResponse.TrackingToken

        GetCrmService.Execute(objSend)

    End Sub

    ''' <summary>
    ''' Routes an e-mail from one queue to another
    ''' </summary>
    ''' <param name="EMail">The e-mail to route</param>
    ''' <param name="SourceQueue">Sourec queue</param>
    ''' <param name="TargetQueue">Target queue</param>
    Public Sub RouteEMail(ByVal EMail As email, ByVal SourceQueue As String, ByVal TargetQueue As String)

        Dim objRoute As New RouteRequest
        Dim objTargetEMail As New TargetQueuedEmail

        objTargetEMail.EntityId = EMail.activityid.Value
        objRoute.Target = objTargetEMail
        objRoute.EndpointId = New Guid(TargetQueue)
        objRoute.RouteType = RouteType.Queue
        objRoute.SourceQueueId = New Guid(SourceQueue)
        GetCrmService.Execute(objRoute)

    End Sub

    ''' <summary>
    ''' Routes a case from one queue to another
    ''' </summary>
    ''' <param name="CaseToRoute">The case to route</param>
    ''' <param name="SourceQueue">The source queue</param>
    ''' <param name="TargetQueue">The target queue</param>
    ''' <remarks></remarks>
    Public Sub RouteCase(ByVal CaseToRoute As Guid, ByVal SourceQueue As String, ByVal TargetQueue As String)

        Dim objRoute As New RouteRequest
        Dim objTargetCase As New TargetQueuedIncident

        objTargetCase.EntityId = CaseToRoute
        objRoute.Target = objTargetCase
        objRoute.EndpointId = New Guid(TargetQueue)
        objRoute.RouteType = RouteType.Queue
        objRoute.SourceQueueId = New Guid(SourceQueue)
        GetCrmService.Execute(objRoute)

    End Sub

    ''' <summary>
    ''' Returns a dynamic entity
    ''' </summary>
    ''' <param name="EntityName">Name of the entity</param>
    ''' <param name="ID">ID of the entity</param>
    Public Function GetDynamicEntity(ByVal EntityName As String, ByVal ID As String) As DynamicEntity

        Dim objRetrieve As New TargetRetrieveDynamic
        Dim objRequest As New RetrieveRequest
        Dim objResponse As RetrieveResponse

        'Set the properties of the target.
        objRetrieve.EntityName = EntityName
        objRetrieve.EntityId = New Guid(ID)

        'Set the properties of the request object.
        objRequest.Target = objRetrieve
        objRequest.ColumnSet = New AllColumns

        ' Indicate that the BusinessEntity should be retrieved as a DynamicEntity.
        objRequest.ReturnDynamicEntities = True

        'Execute the request.
        objResponse = CType(GetCrmService.Execute(objRequest), RetrieveResponse)

        'Extract the DynamicEntity from the request.
        If objResponse.BusinessEntity IsNot Nothing Then
            Return CType(objResponse.BusinessEntity, DynamicEntity)
        Else
            Return Nothing
        End If

    End Function

    ''' <summary>
    ''' Returns a single entity using its GUID
    ''' </summary>
    ''' <param name="strEntity">Type of entity</param>
    ''' <param name="objID">The ID</param>
    ''' <param name="strCols">The columns to return</param>
    Public Function GetSingleData(ByVal strEntity As String, _
                                  ByVal objID As Guid, _
                                  ByVal strCols As String()) As BusinessEntity

        Dim objCols As ColumnSet

        'Now we define the columns to return.
        If strCols IsNot Nothing AndAlso strCols.Length > 0 Then
            objCols = New ColumnSet
            objCols.Attributes = strCols

            'Execute the query
            Return GetCrmService.Retrieve(strEntity, objID, objCols)
        Else
            'Execute the query
            Return GetCrmService.Retrieve(strEntity, objID, New AllColumns)
        End If

    End Function

End Class
